module RComp
    module DotNet
        class CompilerTask < ::Rake::TaskLib
            # Name of compile task. (default is :compile)
            attr_accessor :name

            # Source files as input to the compiler
            attr_accessor :inputs

            # Directories in which to search for assemblies
            attr_accessor :imports

            # Assemblies to reference
            attr_accessor :references

            # Location of .NET Framework
            attr_accessor :framework_path

            # Whether or not compiler is found in Framework directory
            def found_in_framework?
                @found_in_framework
            end
            attr_writer :found_in_framework

            # Compiler executable
            attr_accessor :exe

            # Generate debug files
            attr_accessor :debug

            # Target assembly type: exe (default), winexe, library/dll, module
            attr_accessor :target

            # Options sent to the compiler
            attr_accessor :compiler_options

            # Name of the output file
            attr_accessor :out

            def initialize(name=:compile, exe="not_given", found_in_framework=true)
                @name = name
                @exe = exe
                @inputs, @references, @imports = [], [], []
                @found_in_framework = found_in_framework
                @target = "exe"
                @debug = false
                @out = nil
                @framework_path = ENV["windir"] + "/Microsoft.NET/Framework/v2.0.50727"
                @compiler_options = {}
                yield self if block_given?
                define
            end

            # Create the tasks defined by this task lib.
            def define
                task name do
                    do_task
                end
                self
            end

            def do_task
                return if inputs.empty?

                cmd = ("#{framework_path}/#{exe} " if found_in_framework?) || "#{exe} "

                # Add compiler options
                add_switch :nologo
                add_option :target, target
                add_option :debug, debug
                add_option :out, out if out
                references.each { |r| add_option :reference, r }
                imports.each { |i| add_option :import, i }
                cmd << options.sort.join(" ")

                # Add source files
                cmd << " " << inputs.join(" ")

                # Compile
                unless Kernel.system cmd
                    STDERR.puts "Command '#{cmd}' failed."
                end
            end

            def add_option(name, value)
                case name
                    when :debug
                        opt = (Option.new("debug+") if value) || Option.new("debug-")
                    when :import
                        opt = Option.new("lib", value)
                        name = value.hash
                    when :reference
                        opt = Option.new(name, value)
                        name = value.hash
                    else
                        opt = Option.new(name, value)
                end

                compiler_options[name] = opt.to_s
                self
            end

            def add_switch(name)
                add_option name, nil
            end

            def options
                compiler_options.map { |k, v| v }
            end
        end
    end
end
